﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Serialization;
using System.Diagnostics;
using System.Collections;
using Rex;
using ElisaHippo.Properties;


namespace ElisaHippo
{
    public partial class ScanningGunInput : Form
    {

            List<BarcodesInfo> infos = new List<BarcodesInfo>();
            HashSet<string> allbarcodes = new HashSet<string>();
            Dictionary<string, List<string>> barcode_Tests = new Dictionary<string, List<string>>();
            string sScanFile = Utility.Configures.PosIDResult;
            Point lastCell = new Point(0,0);
            bool bExecuteResultOk = false;
            bool isPosID = false;
            int gridStartPos = 1;
            int sampleCount = 10;
            private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
            
            public ScanningGunInput(bool bPosID)
            {
                InitializeComponent();
                txtSampleCount.Text = sampleCount.ToString();
                txtStartGrid.Text = gridStartPos.ToString();
                isPosID = bPosID;
                
                if (bPosID)
                {
                    DisableSettingGroup();
                    ReadPosIDResult();
                }
                try
                {
                    ReadLims();
                }
                catch(Exception ex)
                {
                    SetInfo(System.Drawing.Color.Red, "读取Lims信息失败： " + ex.Message + "\r\n 请退出程序。");
                    btnConfirm.Enabled = false;
                }
                this.Load += new EventHandler(FormInput_Load);
            }

            private void DisableSettingGroup()
            {
                grpSetting.Enabled = false;
            }

           
            public void InitGrid()
            {
                dataGridView1.Columns.Clear();
                dataGridView1.Rows.Clear();
                allbarcodes.Clear();

                HashSet<int> gridNames = new HashSet<int>();
                foreach (BarcodesInfo info in infos)
                {
                    gridNames.Add(info.grid);
                }

                List<int> gridNameList = gridNames.ToList();
                for (int i = 0; i < gridNames.Count; i++)
                {
                    DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
                    column.HeaderText = string.Format("条{0}", gridNameList[i]);
                    dataGridView1.Columns.Add(column);
                }


                //set txtStartGrid
                txtStartGrid.Text = gridNameList[0].ToString();
                txtSampleCount.Text = infos.Count.ToString();


                for (int i = 0; i < 16; i++)
                {
                    dataGridView1.Rows.Add();
                    dataGridView1.Rows[i].HeaderCell.Value = string.Format("行{0}", i + 1);
                }
                dataGridView1.RowHeadersWidth = 80;

                int curIndex = 0;
                for (int i = 0; i < infos.Count; i++)
                {
                    int column = infos[i].grid - infos[0].grid;
                    int row = infos[i].index - 1;
                    string sBarcode = infos[i].barcode;
                    dataGridView1.Rows[row].Cells[column].Value = sBarcode;
                    string errMsg;
                    if (IsValidBarcode(sBarcode,out errMsg,row,column))
                    {

                        allbarcodes.Add(sBarcode);
                        dataGridView1.Rows[row].Cells[column].Style.BackColor = System.Drawing.Color.LightGreen;
                        dataGridView1.Rows[row].Cells[column].ReadOnly = true;
                    }
                    else
                    {
                        SetCellState(dataGridView1.Rows[row].Cells[column], false);
                        SetInfo(Color.Red, errMsg);
                    }

                    curIndex++;
                }
            }

            private void SetCellState(DataGridViewCell cell, bool ok)
            {
                Color cellColor = ok ? System.Drawing.Color.Green : Color.DarkOrange;
                cell.Style.ForeColor = cellColor;
            }

            private bool IsValidBarcode(string sBarcode, out string errMsg, int curRow = -1, int curCol = -1)
            {
                if (sBarcode == "" || sBarcode == null)
                {
                    errMsg = "条码不能为空！";
                    return false;
                }
                


                int testRow = curRow == -1 ? dataGridView1.CurrentCell.RowIndex : curRow;
                int testCol = curCol == -1 ? dataGridView1.CurrentCell.ColumnIndex : curCol;

                string regStr = Utility.Configures.BarcodeRegex;
                RexSettings settings = new RexSettings(regStr) { k = 1, encoding = CharacterEncoding.ASCII };
                string validStr = RexEngine.GenerateMembers(settings).First();
                
                string sErrRow = dataGridView1.Rows[testRow].HeaderCell.Value.ToString();
                string sErrCol = dataGridView1.Columns[testCol].HeaderCell.Value.ToString();
                Regex reg = new Regex(regStr);
                string sErrPos = string.Format("{0},{1}错误： ", sErrCol, sErrRow);
                if (!reg.IsMatch(sBarcode))
                {
                    errMsg = sErrPos + string.Format("条码格式必须形如: {0}。",validStr);
                    return false;
                }

                 if (allbarcodes.Contains(sBarcode))
                {
                    for (int row = 0; row < dataGridView1.Rows.Count; row++)
                    {
                        for (int col = 0; col < dataGridView1.Rows[row].Cells.Count; col++)
                        {
                            //只测试当前样品前面的样品
                            if (col * 16 + row >= testCol * 16 + testRow)
                                continue;
                            if (dataGridView1.Rows[row].Cells[col].Value.ToString() == sBarcode)
                            {
                                string sCurRowHeader = dataGridView1.Rows[row].HeaderCell.Value.ToString();
                                string sCurColHeader = dataGridView1.Columns[col].HeaderCell.Value.ToString();
                                errMsg = string.Format("条形码已经存在于：{0}，{1}", sCurColHeader, sCurRowHeader);
                                return false;
                            }
                        }
                    }
                }
                errMsg = "";
                return true;
            }

            void FormInput_Load(object sender, EventArgs e)
            {
                if( isPosID)
                    InitGrid();
                else
                    UpdateDataGridView();

                dataGridView1.BeginEdit(false);
            }
            private void ReadLims()
            {
                log.Info("Read lims");
                StreamReader str = new StreamReader( Utility.Configures.LimsPath);
                XmlSerializer xSerializer = new XmlSerializer(typeof(worklist));
                worklist workList = (worklist)xSerializer.Deserialize(str);
                Debug.Assert(workList != null);
                foreach( worklistRequest wr in workList.Items)
                {
                    //wr.barcode
                    List<string> tests = (from t in wr.assayrequest select t.assay).ToList();
                    barcode_Tests.Add(wr.barcode, tests);

                }
            }
            private void ReadPosIDResult()
            {
                log.Info("Read Pos ID result.");
                string sContent = "";
                infos.Clear();
                using (StreamReader sr = new StreamReader(sScanFile))
                {
                    sr.ReadLine();
                    while ((sContent = sr.ReadLine()) != null)
                    {
                        infos.Add(ParseString(sContent));

                    }
                }
            }

            private BarcodesInfo ParseString(string sContent)
            {
                string[] strs = sContent.Split(',');
                BarcodesInfo info = new BarcodesInfo();
                info.grid = int.Parse(strs[0]);
                info.index = int.Parse(strs[2]);
                info.barcode = strs[6];
                return info;
            }

            private void btnConfirm_Click(object sender, EventArgs e)
            {
                log.Info("Confirm clicked.");
                bExecuteResultOk = false;
                string sErrMsg = "";
                DataGridViewCell curCell;
                string sCurBarcode;
                List<string> allBarcodes = new List<string>();
                
                int totalCount = GetTotalCount();
                for (int row = 0; row < dataGridView1.Rows.Count; row++)
                {
                    for (int col = 0; col < dataGridView1.Rows[row].Cells.Count; col++)
                    {
                        bool bValid = false;

                        if (col * 16 + row + 1 > totalCount)
                            continue;
                        curCell = dataGridView1.Rows[row].Cells[col];
                        if (curCell.Value == null)
                        {
                            dataGridView1.Rows[row].Cells[col].Style.BackColor = Color.Yellow;
                            SetInfo(Color.Red, string.Format("单元格为空！"));
                            return;
                        }
                        
                        sCurBarcode = curCell.Value.ToString();
                        bValid = IsValidBarcode(sCurBarcode, out sErrMsg, row, col);

                        if (!bValid)
                        {
                            dataGridView1.Rows[row].Cells[col].Style.BackColor = Color.Yellow;
                            SetInfo(Color.Red, sErrMsg);
                            return;
                        }
                        else
                        {
                            allBarcodes.Add(sCurBarcode);
                        }
                    }

                }

                bool bExist = CheckBarcodeExistsInLims(allBarcodes);
                if (!bExist)
                    return;
                //convert from barcode-tests to test-barcodes
                HashSet<string> allTest = GetAllTests(allBarcodes);
                Dictionary<string, HashSet<string>> test_barcodes = new Dictionary<string, HashSet<string>>();
                foreach (string sTest in allTest)
                {
                    test_barcodes.Add(sTest, new HashSet<string>());
                }

                foreach (string sBarcode in barcode_Tests.Keys)
                {
                    foreach (string sTest in barcode_Tests[sBarcode])
                    {
                        test_barcodes[sTest].Add(sBarcode);
                    }
                }
                Utility.SaveSetting(dataGridView1, infos.Count);
                Utility.CreateTestFiles(test_barcodes);
                bExecuteResultOk = true;
                this.Close();
            }

            private HashSet<string> GetAllTests(List<string> allBarcodes)
            {
                HashSet<string> allTests = new HashSet<string>();
                foreach (string sBarcode in barcode_Tests.Keys)
                {
                    allTests.UnionWith(barcode_Tests[sBarcode]);
                }
                return allTests;
            }

            private bool CheckBarcodeExistsInLims(List<string> allbarcodes)
            {
                bool bAllExists = true;
                int curIndex = 0;
                string errBarcode = "";
                foreach (string sBarcode in allbarcodes)
                {
                    if (!barcode_Tests.ContainsKey(sBarcode))
                    {
                        errBarcode = sBarcode;
                        bAllExists = false;
                        break;
                    }
                    curIndex++;
                }
                if (!bAllExists)
                {
                    int row = curIndex % 16;
                    int col = curIndex / 16;
                    SetInfo(Color.Red, string.Format("位于{0} 行：{1}的单元格的条形码{2}未能在Lims中找到！", dataGridView1.Columns[col].HeaderText, col + 1, errBarcode));
                    return false;
                }
                return true;
                
            }


            void SetInfo(System.Drawing.Color color, string text)
            {

                txtInfo.BackColor = Color.White;
                txtInfo.ForeColor = color;
                txtInfo.Text = text;
            }
            void SetInfo(string text, System.Drawing.Color color)
            {

                txtInfo.BackColor = Color.White;
                txtInfo.ForeColor = color;
                txtInfo.Text = text;
            }

            #region events for datagridview
      
            private DataGridViewCell FindEditableCell()
            {
                DataGridViewCell curCell = dataGridView1.CurrentCell;
                int row, col;
                row = curCell.RowIndex;
                col = curCell.ColumnIndex;
      
                do
                {
                    if (row < 15)
                        row++;
                    else
                    {
                        row = 0;
                        if (col < dataGridView1.Columns.Count - 1)
                            col++;
                        else
                            break;
                    }
                    if (!dataGridView1.Rows[row].Cells[col].ReadOnly)
                        break;

                } while (true);

                return dataGridView1.Rows[row].Cells[col];
            }

            private bool  ValidateCell(DataGridViewCell cell2Validate)
            {  
                //dont't check for the one exceed total count;
                if (cell2Validate.ReadOnly)
                    return true;
                
                int totalCount = GetTotalCount();
                if (cell2Validate.ColumnIndex * 16 + cell2Validate.RowIndex + 1 > totalCount)
                    return true;

                string s = cell2Validate.EditedFormattedValue.ToString();
                string sErrMsg;
                if (!IsValidBarcode(s, out sErrMsg))
                {
                    SetInfo(Color.Red, sErrMsg);
                    cell2Validate.Style.BackColor = Color.LightPink;
                    return false;
                }
                else
                {
                    //找出该样品之前的barcode
                    string oldBarcode = UpdateBarcode(cell2Validate.RowIndex, cell2Validate.ColumnIndex, s);
                    allbarcodes.Remove(oldBarcode);
                    allbarcodes.Add(s);
                    SetInfo(Color.Black, "");
                    cell2Validate.Style.ForeColor = Color.Green;
                    cell2Validate.Style.BackColor = Color.White;
                }
                return true;

            }

            private void dataGridView1_KeyPress(object sender, KeyPressEventArgs e)
            {
                //delete selected barcodes
                if (e.KeyChar == char.Parse("-"))
                {
                    foreach (DataGridViewCell cell in dataGridView1.SelectedCells)
                        cell.Value = "";
                    return;
                }

                if (e.KeyChar != char.Parse("+"))
                    return;

                if (dataGridView1.CurrentCell == null)
                    return;

                bool bok = ValidateCell(dataGridView1.CurrentCell);
                if (bok)
                    dataGridView1.CurrentCell = FindEditableCell();
                
                    
            }

       
            #endregion
            private int GetTotalCount()
            {
                 return isPosID ? infos.Count : sampleCount;
            }

            private string UpdateBarcode(int row, int col, string s)
            {
                string sOld = "";
                for (int i = 0; i < infos.Count; i++)
                {
                    if (infos[i].grid == col && infos[i].index == row)
                    {
                        sOld = infos[i].barcode;
                        BarcodesInfo tmp = new BarcodesInfo(infos[i]);
                        tmp.barcode = s;
                        infos.RemoveAt(i);
                        infos.Add(tmp);
                    }

                }
                return sOld;
            }

            #region auto complete
            private void FillTheCells(List<DataGridViewCell> selCells ,int num2Complete)
            {
                Permutation permutaion = new Permutation();
                bool finished = false;
                List<string> strs = permutaion.GetPossibleBarcodes(selCells[0].Value.ToString(), selCells[1].Value.ToString(), num2Complete);
                int colStart = selCells[1].ColumnIndex;
                int rowStart = selCells[1].RowIndex;
                int startCellID = colStart * 16 + rowStart;
                int curAutoCellIndex = 0;
               
                for (int col = colStart; col < dataGridView1.Columns.Count; col++)
                {
                    for (int row = 0; row < dataGridView1.Rows.Count; row++)
                    {
                        if( row + col*16 <= startCellID)
                            continue;

                        string sTmpBarcode = strs[curAutoCellIndex++];
                        dataGridView1.Rows[row].Cells[col].Value = sTmpBarcode;
                        ValidateCell(dataGridView1.Rows[row].Cells[col]);
                        if (curAutoCellIndex == num2Complete)
                        {
                            finished = true;
                            break;
                        }
                    }

                    if (finished)
                        break;
                }

            }



            private bool CheckSelCellsFormat(DataGridViewCell firstCell, DataGridViewCell secondCell, out string errMsg)
            {
                //check the selected cell's format
                int row = firstCell.RowIndex;
                int col = firstCell.ColumnIndex;
                if (!IsValidBarcode(firstCell.Value.ToString(), out errMsg, row, col))
                {
                    errMsg = "首单元格的条形码格式不正确： " + errMsg;
                    //SetInfo(Color.Red, "首单元格的条形码格式不正确： " + sErrMsg);
                    return false;
                }

                row = secondCell.RowIndex;
                col = secondCell.ColumnIndex;
                if (!IsValidBarcode(secondCell.Value.ToString(), out errMsg, row, col))
                {
                    errMsg = "次单元格的条形码格式不正确： " + errMsg;
                    //SetInfo(Color.Red, "首单元格的条形码格式不正确： " + sErrMsg);
                    return false;
                }

                if (firstCell.Value.ToString().Length != secondCell.Value.ToString().Length)
                {
                    errMsg = "首单元格与次单元格的条码长度不一致。";
                    return false;
                }

                

                int nDiffCharNum = 0;
                string sFirstCell = firstCell.Value.ToString();
                string sSecondCell = secondCell.Value.ToString();
                for (int i = 0; i < sFirstCell.Length; i++)
                {
                    char chr1 = sFirstCell[i];
                    char chr2 = sSecondCell[i];
                    if (chr1 != chr2)
                    {
                        nDiffCharNum++;

                        if(!IsSameType(chr1,chr2))
                        {
                            errMsg = string.Format("首单元格与次单元格的字符在第{0}处不匹配，必须同为数字、小写字母或大写字母。", i + 1);
                            return false;
                        }
                        if (chr2 < chr1)
                        {
                            errMsg = "次单元格的条码不得小于首单元格的条码！";
                            return false;
                        }

                        if (chr2 - chr1 != 1)
                        {
                            errMsg = "次单元格的条码与首单元格的条码必须邻接！";
                            return false;
                        }
                    }
                }

                if (nDiffCharNum > 1)
                {
                    errMsg = "首单元格与次单元格不同的字符数大于1，无法推断其余单元格的内容。";
                    return false;
                }
                return true;
            }

            #region test char is same type
         

            private bool IsSameType(char chr1,char chr2)
            {
                if (Char.IsDigit(chr1))
                    return Char.IsDigit(chr2);

                if (Char.IsLower(chr1))
                    return Char.IsLower(chr2);

                if (Char.IsUpper(chr1))
                    return Char.IsUpper(chr2);
                return chr1 == chr2;
            }
            #endregion


            #endregion

        

      

            private void ScanningGunInput_FormClosed(object sender, FormClosedEventArgs e)
            {
                log.Info("Form closed");
                Utility.WriteExecuteResult(bExecuteResultOk);
            }

            private void btnSet_Click(object sender, EventArgs e)
            {
                int i = 0;
                bool bok = int.TryParse(txtStartGrid.Text, out i);
                if (!bok)
                    SetInfo("Grid起始号必须为数字！", Color.Red);
                if (i < 1 || i > 30)
                {
                    bok = false;
                    SetInfo("Grid起始位置必须位于1到30之间", Color.Red);
                }
                if (!bok)
                {
                    txtStartGrid.Text = gridStartPos.ToString();
                    return;
                }
                gridStartPos = i;

                bok = int.TryParse(txtSampleCount.Text, out i);
                if (!bok)
                    SetInfo("样品数量必须为数字！", Color.Red);
                if (i <= 0 || i > 192)
                {
                    SetInfo("样品数量必须介于0和192之间", Color.Red);
                    bok = false;
                }

                if (!bok)
                {
                    txtSampleCount.Text = sampleCount.ToString();
                    return;
                }
                sampleCount = i;

                UpdateDataGridView();
            }

            private void UpdateDataGridView()
            {
                dataGridView1.AllowUserToAddRows = false;
                dataGridView1.Columns.Clear();
                List<string> strs = new List<string>();

                int colNum = (int)Math.Ceiling(sampleCount / 16.0);
                for (int j = 0; j < colNum; j++)
                    strs.Add("");

                for (int i = 0; i < colNum; i++)
                {
                    DataGridViewTextBoxColumn column = new DataGridViewTextBoxColumn();
                    column.HeaderText = string.Format("条{0}", gridStartPos + i);
                    dataGridView1.Columns.Add(column);

                    //dataGridView.Columns[i].HeaderCell.Value = string.Format("Grid{0}", i + 1);
                }

                dataGridView1.RowHeadersWidth = 80;
                for (int i = 0; i < 16; i++)
                {
                    dataGridView1.Rows.Add(strs.ToArray());
                    dataGridView1.Rows[i].HeaderCell.Value = string.Format("行{0}", i + 1);
                }
            }

            private void btnComplete_Click(object sender, EventArgs e)
            {
                int num2Complete;
                bool bInputOk = int.TryParse(txtCells2CompleteCount.Text, out num2Complete);
                if (!bInputOk)
                {
                    SetInfo("补齐数量必须为数字",Color.Red);
                    return;
                }
                if (num2Complete <= 0)
                {
                    SetInfo("补齐数量必须大于0", Color.Red);
                    return;
                }

                if (dataGridView1.SelectedCells.Count != 2)
                {
                    SetInfo("需选择两个单元格作为后续单元格的补齐模板。", Color.Red);
                    return;
                }

                List<DataGridViewCell> selCells = new List<DataGridViewCell>();
                foreach (DataGridViewCell cell in dataGridView1.SelectedCells)
                {
                    selCells.Add(cell);
                }

                //sort the selected cells by column then row index
                selCells = selCells.OrderBy(x => x.ColumnIndex * 16 + x.RowIndex).ToList();
                //DataGridViewCell firstCell = selCells.First();

                string errMessage = "";
                bool bRightFormat = CheckSelCellsFormat(selCells[0], selCells[1], out errMessage);
                if (!bRightFormat)
                {
                    SetInfo(Color.Red, errMessage);
                    return;
                }

                FillTheCells(selCells,num2Complete);      
            }

            private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
            {
                //dataGridView1.BeginEdit(false);
            }

    

           

    
          
       

         
           
          
        }

        struct BarcodesInfo
        {
            public int grid;
            public int index;
            public string barcode;
            public BarcodesInfo(int g, int i, string b)
            {
                grid = g;
                index = i;
                barcode = b;
            }

            public BarcodesInfo(BarcodesInfo info)
            {
                grid = info.grid;
                index = info.index;
                barcode = info.barcode;
            }
        }
    
}
